package com.controller;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.PostConstruct;
import javax.faces.application.FacesMessage;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ManagedProperty;
import javax.faces.bean.ViewScoped;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;
import javax.servlet.http.HttpServletRequest;

import org.apache.log4j.Logger;
import org.jfree.util.Log;
import org.primefaces.event.FileUploadEvent;
import org.primefaces.model.DefaultStreamedContent;
import org.primefaces.model.LazyDataModel;
import org.primefaces.model.SortOrder;
import org.primefaces.model.StreamedContent;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.web.jsf.FacesContextUtils;

import com.builder.EnvaseBuilder;
import com.builder.FamiliaBuilder;
import com.builder.MarcaBuilder;
import com.builder.ProductoBuilder;
import com.builder.UnidadDeMedidaBuilder;
import com.dao.BaseModelDAO;
import com.model.Envase;
import com.model.Familia;
import com.model.Marca;
import com.model.Parametro;
import com.model.Producto;
import com.model.UnidadDeMedida;
import com.query.ProductoQuery;
import com.util.FilesystemUtil;
import com.util.JSFUtil;
import com.util.ValidatorUtil;
import com.view.EnvaseView;
import com.view.FamiliaView;
import com.view.MarcaView;
import com.view.ProductoView;
import com.view.UnidadDeMedidaView;

@ManagedBean
@ViewScoped
@SuppressWarnings("serial")
public class ProductoController extends PaginableController<Producto> {
	
	private static final String directorioPorDefecto = "C:\\";
	private static final String directorioConfigurado = "directorioFotos";

	private Logger log = Logger.getLogger(ProductoController.class);
	private BaseModelDAO<Producto> dao;
	private Producto producto;
	private ProductoQuery productoQuery;

	private List<UnidadDeMedidaView> listaDeUnidadDeMedida;
	private List<FamiliaView> listaDeFamilia;
	private List<MarcaView> listaDeMarca;
	private List<EnvaseView> listaDeEnvase;

	private BaseModelDAO<UnidadDeMedida> daoUnidadDeMedida;
	private BaseModelDAO<Familia> daoFamilia;
	private BaseModelDAO<Marca> daoMarca;
	private BaseModelDAO<Envase> daoEnvase;
	private BaseModelDAO<Parametro> daoParametro;
	
	private boolean esmodificacion;
	private boolean haymodificacionPendiente;
	private String rutaArchivoOriginal;
	private String nombreArchivoOriginal;
	
	private String rutaDirectorioFotos;
	private StreamedContent imagen;

	@ManagedProperty("#{productoView}")
	private ProductoView productoView;

	@ManagedProperty("#{productoBuilder}")
	private ProductoBuilder productoBuilder;

	@ManagedProperty("#{unidadDeMedidaBuilder}")
	private UnidadDeMedidaBuilder unidadDeMedidaBuilder;

	@ManagedProperty("#{familiaBuilder}")
	private FamiliaBuilder familiaBuilder;

	@ManagedProperty("#{marcaBuilder}")
	private MarcaBuilder marcaBuilder;

	@ManagedProperty("#{envaseBuilder}")
	private EnvaseBuilder envaseBuilder;

	@SuppressWarnings("unchecked")
	public ProductoController() {
		try {
			dao = (BaseModelDAO<Producto>) FacesContextUtils
					.getWebApplicationContext(FacesContext.getCurrentInstance())
					.getBean("productoDAO");

			daoUnidadDeMedida = (BaseModelDAO<UnidadDeMedida>) FacesContextUtils
					.getWebApplicationContext(FacesContext.getCurrentInstance())
					.getBean("unidadDeMedidaDAO");

			daoFamilia = (BaseModelDAO<Familia>) FacesContextUtils
					.getWebApplicationContext(FacesContext.getCurrentInstance())
					.getBean("familiaDAO");

			daoMarca = (BaseModelDAO<Marca>) FacesContextUtils
					.getWebApplicationContext(FacesContext.getCurrentInstance())
					.getBean("marcaDAO");

			daoEnvase = (BaseModelDAO<Envase>) FacesContextUtils
					.getWebApplicationContext(FacesContext.getCurrentInstance())
					.getBean("envaseDAO");
			
			daoParametro = (BaseModelDAO<Parametro>) FacesContextUtils
					.getWebApplicationContext(FacesContext.getCurrentInstance())
					.getBean("parametroDAO");
			
			Parametro parametro = daoParametro.get(directorioConfigurado);

			if (parametro.getValor() != null
					&& !parametro.getValor().equals("")) {
				rutaDirectorioFotos = parametro.getValor();
			} else {
				rutaDirectorioFotos = directorioPorDefecto;
			}

			esmodificacion = false;
			haymodificacionPendiente = false;
			productoQuery = new ProductoQuery();
			addEdit = false;
			
		} catch (Throwable e) {
			log.error("Error al inicializar la clase ProductoController", e);
			FacesContext.getCurrentInstance().addMessage(
					null,
					new FacesMessage(FacesMessage.SEVERITY_ERROR,
							"Error al realizar la operacion", ""));
		}
	}

	@PostConstruct
	public void inicializar() {

		listaDeFamilia = new ArrayList<FamiliaView>();
		listaDeFamilia.addAll(familiaBuilder.toView(daoFamilia.getList()));

		listaDeUnidadDeMedida = new ArrayList<UnidadDeMedidaView>();
		listaDeUnidadDeMedida.addAll(unidadDeMedidaBuilder
				.toView(daoUnidadDeMedida.getList()));

		listaDeMarca = new ArrayList<MarcaView>();
		listaDeMarca.addAll(marcaBuilder.toView(daoMarca.getList()));

		listaDeEnvase = new ArrayList<EnvaseView>();
		listaDeEnvase.addAll(envaseBuilder.toView(daoEnvase.getList()));

	}

	public void query(ActionEvent event) {
		loadList();
	}

	public void edit(ActionEvent event) {
		try {
			clear();
			producto = (Producto) lazyDM.getRowData();
			producto = dao.findFULL(producto.getID());
			productoView = productoBuilder.toView(producto);
			esmodificacion = true;
			rutaArchivoOriginal = productoView.getRutaArchivo();
			nombreArchivoOriginal = productoView.getNombreArchivo();
			addEdit = true;

		} catch (Throwable e) {
			log.error("Error al editar", e);
			FacesContext.getCurrentInstance().addMessage(
					null,
					new FacesMessage(FacesMessage.SEVERITY_ERROR,
							"Error al realizar la operacion", ""));
		}
	}

	public void delete(ActionEvent event) {
		try {
			producto = (Producto) lazyDM.getRowData();
			dao.delete(producto);
			loadList();
		} catch (Throwable e) {
			log.error("Error al eliminar", e);
			FacesContext.getCurrentInstance().addMessage(
					null,
					new FacesMessage(FacesMessage.SEVERITY_ERROR,
							"Error al realizar la operacion", ""));
		}
	}

	public void add(ActionEvent event) {
		addEdit = true;
		clear();
	}

	public void save(ActionEvent event) {
		try {

			List<String> errores = new ValidatorUtil().validate(productoView);

			if (errores.size() == 0) {

				producto = productoBuilder.toDomain(productoView);

				if (producto.getID() != null) {
					dao.edit(producto);
					addEdit = false;
				} else {
					dao.save(producto);
				}
				clear();
				JSFUtil.saveMessage("Elemento guardado con exito",
						FacesMessage.SEVERITY_INFO);

				if (!addEdit) {
					loadList();
				}
			} else {
				JSFUtil.saveMessages(errores);
			}

		} catch (DataIntegrityViolationException e) {
			JSFUtil.saveMessage(
					"Error al guardar: El detalle debe ser unico en el sistema.",
					FacesMessage.SEVERITY_ERROR);
		} catch (Throwable e) {
			log.error("Error al guardar", e);
			FacesContext.getCurrentInstance().addMessage(
					null,
					new FacesMessage(FacesMessage.SEVERITY_ERROR,
							"Error al realizar la operacion", ""));
		}
	}

	public void cancel(ActionEvent event) {
		addEdit = false;
		lazyDM = null;
		
		if(haymodificacionPendiente){
			revertirCambioDeArchivo();
			clear();
		}
	}
	
	private void revertirCambioDeArchivo(){
		try {
			FilesystemUtil.borrarArchivo(productoView.getRutaArchivo(),
					productoView.getNombreArchivo());
		} catch (Exception e) {
			Log.error("Error al borrar el archivo adjunto: ", e);
		}
	}

	public void clear() {
		producto = new Producto();
		productoView = new ProductoView();
		esmodificacion = false;
		haymodificacionPendiente = false;
	}

	private void loadList() {

		final Marca unaMarca = daoMarca.find(1L);

		lazyDM = new LazyDataModel<Producto>() {
			@Override
			public List<Producto> load(int first, int pageSize,
					String sortField, SortOrder sortOrder,
					Map<String, Object> filters) {

				Map<String, Object> filtro = new HashMap<String, Object>();

				filtro.put("detalle", productoQuery.getDetalle());
				filtro.put("familia.id", productoQuery.getFamiliaID());
				filtro.put("marca", unaMarca);

				List<Producto> lista = null;

				try {
					lista = dao.getList(first, pageSize, "detalle", true,
							filtro, true);

				} catch (Throwable t) {
					log.error("Error en: ", t);
				}

				return lista;
			}
		};

		Map<String, Object> filtro = new HashMap<String, Object>();
		filtro.put("detalle", productoQuery.getDetalle());
		filtro.put("familia.id", productoQuery.getFamiliaID());
		filtro.put("marca", unaMarca);

		lazyDM.setRowCount(dao.count(filtro, true).intValue());
	}
	
	public void handleFileUpload(FileUploadEvent event) {

		JSFUtil.saveMessage(event.getFile().getFileName()
				+ " cargado correctamente.", FacesMessage.SEVERITY_INFO);
		productoView.setImagen(event.getFile());

		try {
			imagen = new DefaultStreamedContent(event.getFile()
					.getInputstream(), "image/jpeg");
			
			if(esmodificacion && productoView.getNombreArchivo() != null && !productoView.getNombreArchivo().equals("")){
				haymodificacionPendiente = true;
			}
			
			String nombre = "foto" + new Date().getTime() + ".jpg";
			if (productoView.getImagen() != null) {
				try {
					FilesystemUtil.escribirArchivo(rutaDirectorioFotos, nombre,
							productoView.getImagen().getContents());
				} catch (Exception e) {
					Log.error("Error al guardar el archivo adjunto: ", e);
				}
			}
			productoView.setRutaArchivo(rutaDirectorioFotos);
			productoView.setNombreArchivo(nombre);
			
		} catch (IOException e) {
		}
	}

	public void upload(FileUploadEvent event) {
		FacesMessage msg = new FacesMessage("Mensaje", event.getFile()
				.getFileName() + " cargado correctamente.");
		productoView.setImagen(event.getFile());
		FacesContext.getCurrentInstance().addMessage(null, msg);
	}

	public BaseModelDAO<Producto> getDao() {
		return dao;
	}

	public void setDao(BaseModelDAO<Producto> dao) {
		this.dao = dao;
	}

	public Producto getProducto() {
		return producto;
	}

	public void setProducto(Producto producto) {
		this.producto = producto;
	}

	public ProductoQuery getProductoQuery() {
		return productoQuery;
	}

	public void setProductoQuery(ProductoQuery productoQuery) {
		this.productoQuery = productoQuery;
	}

	public ProductoView getProductoView() {
		return productoView;
	}

	public void setProductoView(ProductoView productoView) {
		this.productoView = productoView;
	}

	public ProductoBuilder getProductoBuilder() {
		return productoBuilder;
	}

	public void setProductoBuilder(ProductoBuilder productoBuilder) {
		this.productoBuilder = productoBuilder;
	}

	public List<UnidadDeMedidaView> getListaDeUnidadDeMedida() {
		return listaDeUnidadDeMedida;
	}

	public void setListaDeUnidadDeMedida(
			List<UnidadDeMedidaView> listaDeUnidadDeMedida) {
		this.listaDeUnidadDeMedida = listaDeUnidadDeMedida;
	}

	public List<FamiliaView> getListaDeFamilia() {
		return listaDeFamilia;
	}

	public void setListaDeFamilia(List<FamiliaView> listaDeFamilia) {
		this.listaDeFamilia = listaDeFamilia;
	}

	public List<MarcaView> getListaDeMarca() {
		return listaDeMarca;
	}

	public void setListaDeMarca(List<MarcaView> listaDeMarca) {
		this.listaDeMarca = listaDeMarca;
	}

	public List<EnvaseView> getListaDeEnvase() {
		return listaDeEnvase;
	}

	public void setListaDeEnvase(List<EnvaseView> listaDeEnvase) {
		this.listaDeEnvase = listaDeEnvase;
	}

	public UnidadDeMedidaBuilder getUnidadDeMedidaBuilder() {
		return unidadDeMedidaBuilder;
	}

	public void setUnidadDeMedidaBuilder(
			UnidadDeMedidaBuilder unidadDeMedidaBuilder) {
		this.unidadDeMedidaBuilder = unidadDeMedidaBuilder;
	}

	public FamiliaBuilder getFamiliaBuilder() {
		return familiaBuilder;
	}

	public void setFamiliaBuilder(FamiliaBuilder familiaBuilder) {
		this.familiaBuilder = familiaBuilder;
	}

	public MarcaBuilder getMarcaBuilder() {
		return marcaBuilder;
	}

	public void setMarcaBuilder(MarcaBuilder marcaBuilder) {
		this.marcaBuilder = marcaBuilder;
	}

	public EnvaseBuilder getEnvaseBuilder() {
		return envaseBuilder;
	}

	public void setEnvaseBuilder(EnvaseBuilder envaseBuilder) {
		this.envaseBuilder = envaseBuilder;
	}
	
}
